#ifndef BMD_GOBJS_H
#define BMD_GOBJS_H

#include <cmath>
#include <vector>
#include <iostream>
#include "FreeImage.h"

//the following 3 included for rand(), srand()
#include "math.h"
#include "stdlib.h"
#include "time.h"

using namespace std;

static const int dVOID = 0;
static const int dNORTH = -2;
static const int dEAST = 1;
static const int dSOUTH = 2;
static const int dWEST = -1;

static const int tUNKOWN = -1;
static const int tEMPTY = 0;
static const int tBRICK = 1;
static const int tBLOCK = 2;
static const int tEXPLOSION = 3;
static const int tEXPLOSION_BRICK = 4;


static const float POWERUP_ODDS = (float)0.2;

static const int pVOID = -1;
static const int pBOMBS = 0;
static const int pSPEED = 1;
static const int pPOWER = 2;
static const int pKICKING = 3;
static const int pDETONATION = 4;

static const int pOVER_MAX_INDEX = 5; //whatever the highest powerup index is + 1

enum BomberStatus {bDEAD = -1, bALIVE = 0};


static float NEAR_CRITERION = (float)0.3; //% of duration left before a bomb is considered 'near' an explosion.

//some of these are temporary
static unsigned int BOMBER_STEPS = 6;	//6 steps of motion (time frames) before entering a new tile
static unsigned int BOMB_STEPS = 2;		//bombs are twice as fast

static unsigned int MAX_ALLOWED_BOMBS = 9999;
static unsigned int DEFAULT_BOMBS = 2;
static unsigned int MIN_MOVE_STEPS = 2;
static bool			DEFAULT_KICKING = false;
static bool			DEFAULT_DETONATION = false;

static unsigned int DEFAULT_BOMB_DURATION = 65;
static unsigned int DEFAULT_BOMB_POWER = 2;

static unsigned int STARTING_SCORE = 0;


static unsigned int TIMER_DELAY = 200;


static unsigned int DEFAULT_EXPLOSION_DURATION = 6;

static float DEFAULT_BRICK_DENSITY = (float)0.7;

static const char * fname_PLAYER_DATA = "playerdat.txt";


//random integer from 0 to max - 1 function
static int rint(int max)
{
	if(max > 0)
		return rand() % max;
	return 0;
}

//returns true with probability ~p
static bool prob(float p)
{
	float temp = rand() + 1000.0; //added 1000 to increase "precision"
	return (rint(temp) < p * temp);
}

/*
Used by the vm to convert a "step" value to
the number of ticks requiered to fully move from 
a tile to an adjacent tile.
*/
static int to_vm_movespeed(int step)
{ return 2*step + 1; }


struct Coordinates
{
	unsigned int x;
	unsigned int y;

	Coordinates(){x = y = 0;};
	inline Coordinates(const unsigned int & i, const unsigned int & j){x = i; y = j;}
};



/*
- Movement between two tiles is controlled by the MovementState.
- This object is queried, modified and updated in order to simulate 
the transition between two tiles.

*/
class MovementState
{
private:
	int direction;
	int step;		//what stage of the movement between two locations is the player in?
	int total_steps; //total number of steps needed to enter the next tile.

	/*
		Performs one 'tile' movement on the input 
		coordinates in the 'direction'.
	*/
	void movement(Coordinates & coords)
	{
		if(direction > 0)
		{
			if(direction == dSOUTH)
				coords.y++;
			else
				coords.x++; //east is also positive
		}
		else
		{
			if(direction == dNORTH)
				coords.y--;
			else
				coords.x--; //west is negative
		}
	}

public:

	/*
		Used for tile transitions that 
		also result in a Speed powerup 
		being collected.
	*/
	void set_steps(int in_steps)
	{
		total_steps = in_steps;
		if(step > total_steps)
			step--;
		else if(step < total_steps) //this should always be true
			step++;
	}

	//intializes the mstate
	void init(unsigned int in_t_steps)
	{
		direction = dVOID;
		step = 0;
		total_steps = in_t_steps;
	}

	/*
	Resets the mstate, used mainly for 
	bomb kicking.
	*/
	void reset(int in_direction)
	{
		direction = in_direction;
		step = 0;
	}

	int get_direction()
	{return direction;}

	/*
	Finds which tile is currently reserved by the 
	mstate. Used for when the owner is destroyed.

	Does not modify input.
	*/
	Coordinates reserved(const Coordinates & coords)
	{
		Coordinates ret = coords;
		if(direction * step > 0)
			return dest(ret);
		else if(direction * step < 0)
			return source(ret);
		return ret; //this should not be reachable

	}

	/*
	Finds the coordinates the mstate will 
	lead to if one starts at coords.

	Does not modify input.
	*/
	Coordinates dest(const Coordinates & coords)
	{
		Coordinates ret = coords;
		movement(ret);
		return ret;
	}

	//finds the previous Coordinates that would arive at these, opposite of dest
	Coordinates source(const Coordinates & coords)
	{
		Coordinates ret = coords;
		direction = -1 * direction; //reverse direction
		movement(ret); direction = -1 * direction; //find ret, reset direction
		return ret;
		
	}

	/*
	Performs one "tick" of the movestate.
	This corresponds 1:1 with a "timestep" of the 
	game engine.

	Returns true if tile transitions occur.
	*/
	bool tick()
	{
		if(direction > 0)
		{	
			if(step >= total_steps)
				return true;
			step++;}
		else if(direction < 0)
		{	if(step <= -total_steps)
				return true;
			step--;}
		
		return false; //no movement occured
	}

	/*
	Tests whether the input direction is an allowed movement direction.
	This is related to between tile transition. However because of the implementation 
	of the AI this rarely, if at all, returns false. (AIs only issue orders after 
	tile transitions complete).

	The direction constants are set up such that opposite directions will cause the 
	last expression to evaluate to true.
	*/
	inline bool allowed_redirect(int new_direction)
	{return !step || !direction || (direction + new_direction == 0);} // CHANGE

	/*
	Attempts to change the current direction to a new_direction.

	Returns based on success.
	*/
	bool change_direction(int new_direction)
	{
		if(allowed_redirect(new_direction))
		{	direction = new_direction;
			return true; //successful change
		}
		return false; //succssful change
	}



	//if the player's "step" variable is zero, the player is at the center of a tile
	inline bool is_displaced(){return (step != 0);} //kept giving performance warning


	//once a tile change has occured, this needs to be called
	//
	// updates the MovementState, but also a bomber's coordinates (which are input)
	//
	void perform_tile_change()//Coordinates & in_coords)
	{
		step *= -1;
		//coords = in_coords;
	}

	//being in a certain step of motion, or moving in a direction implies one is moving
	inline bool moving(){return (step || direction);}


	

	//movement fraction used for rendering
	double move_fracX()
	{if(direction == dWEST || direction == dEAST)
			return ((double)step)/(2*total_steps + 1);
		return 0.0;}

	double move_fracY()
	{if(direction == dNORTH || direction == dSOUTH)
			return ((double)step)/(2*total_steps + 1);
		return 0.0;}
};

/*
A struct storin the orders a bomber 
receives from the VM. Used by the main engine loop.
*/
struct BomberOrders
{
	bool bomb_place;
	bool detonate;
	//other on use bools
	int move_direction;

	BomberOrders()
	{	bomb_place = detonate = false;
		move_direction = dVOID;}

	void clear()
	{	bomb_place = detonate = false;
		move_direction = dVOID;}

	bool move_ordered()
	{return (move_direction != 0);}


};


/*
- A struct used to store orders given to bombs 
by their owner bombers.
- Not used by the VM.
*/
struct BombOrders
{
	bool detonate;

	BombOrders()
	{	detonate = false;}
};

/*
Stores behaviour properties of a bomber.
The "power" powerup property is, however, stored in 
BombProperties.
*/
struct BomberProperties
{
	unsigned int max_bombs;
	unsigned int move_steps; //half the number of steps to move to next tile
	bool kicking;
	bool detonation;
	
	BomberProperties()
	{max_bombs = DEFAULT_BOMBS; move_steps = BOMBER_STEPS;
		kicking = DEFAULT_KICKING; detonation = DEFAULT_DETONATION;}
	BomberProperties(unsigned int in_max_bombs, unsigned int in_move_steps)
	{max_bombs = in_max_bombs; move_steps = in_move_steps;
		kicking = DEFAULT_KICKING; detonation = DEFAULT_DETONATION;}

	inline void attempt_inc_bombs()
	{ if(max_bombs < MAX_ALLOWED_BOMBS) max_bombs++;}

	inline bool attempt_inc_move()
	{ 
		if(move_steps > MIN_MOVE_STEPS) 
		{move_steps--; return true;}
		return false;
	}

	inline void give_kicking()
	{ kicking = true;}

	inline void give_detonation()
	{ detonation = true;}

};

/*
Stores behaviour properties of bombs.
*/
struct BombProperties
{
	unsigned int duration;
	unsigned int power;
	unsigned int move_steps;

	BombProperties()
	{duration = DEFAULT_BOMB_DURATION; power = DEFAULT_BOMB_POWER; move_steps = BOMB_STEPS;}
	BombProperties(unsigned int in_duration, unsigned int in_power)
	{duration = in_duration; power = in_power;}

	//used only for power up acquisition at the moment
	void increase_power()
	{power++;}
};


/*
The bomber class.
The major constituents are:

*Movement:
--coords
--mstate
*Poperties:
--prop        //BomberProperties
--bomb_prop   //BombProperties - bombs to be produced
--status      //enum
--n_bombs	  //number of bomber's bombs in play at the moment
*ID
--id
--team_id
*/
class Bomber
{


public:

	int id;
	Coordinates coords;
	MovementState mstate;
	BomberOrders orders;
	BomberProperties prop;
	BombProperties bomb_prop;
	BomberStatus status;  
//	ScoringProperties score;
	unsigned int team_id;
	unsigned int nbombs;

	Bomber(int in_id)
	{
		team_id = 0;
		id = in_id;
		status = bALIVE;
		mstate.init(prop.move_steps);
		orders.clear();
		nbombs = 0;
	}

	inline int get_id(){return id;}
	inline unsigned int get_team_id(){return team_id;}
	inline void set_max_bombs(unsigned int in_max){prop.max_bombs = in_max;}
	bool bombs_available(){return nbombs < prop.max_bombs;}

	void kill(){status = bDEAD;}
	bool is_alive(){return status != bDEAD;}

	void gain_powerup(int ptype)
	{
		switch(ptype)
		{
			case pBOMBS:
				prop.attempt_inc_bombs();
				break;
			case pSPEED:
				if(prop.attempt_inc_move())
					mstate.set_steps(prop.move_steps);
				break;
			case pPOWER:
				bomb_prop.increase_power();
				break;
			case pKICKING:
				prop.give_kicking();
				break;
			case pDETONATION:
				prop.give_detonation();
				break;
			default:
				0;
		}
	}

	//for testing
	//brings a bomber back to life
	void rezz()
	{status = bALIVE;}
};



/*
The bomb class.
The major constituents are:

*Movement:
--coords
--mstate
*Poperties:
--bomb_prop   //BombProperties - bombs
--remaining_duration //how soon will the bomb explode
*ID
--id
--owner_id //who dropped this bomb?
*Status
--gone // has the bomb blown up already? used as an engine flag
*/
class Bomb
{	
	unsigned int remaining_duration;
	unsigned int id;
	unsigned int owner_id; //the ID of the bomber that placed this bomb

public:
	Coordinates coords;
	MovementState mstate;
	BombOrders orders;
	BombProperties prop;
	bool gone;
	//unsigned int team_id;

	Bomb()
	{gone = false; mstate.init(prop.move_steps);}

	Bomb(const unsigned int in_id)
	{
		gone = false;
		id = in_id;
		prop.duration = remaining_duration = 10000;
		mstate.init(prop.move_steps);
	}

	/*
	A function used to set up the id, owner and poperties.
	This was in place for the possibility of powerups that 
	take over opponents bombs, or modify their properties.
	However it is now used as an initialization function only.
	*/
	void set(	const unsigned int & in_id, 
				const unsigned int & in_owner_id, 
				const BombProperties & in_prop)
	{
		gone = false;
		id = in_id;
		owner_id = in_owner_id;
		prop = in_prop;
		remaining_duration = prop.duration;
		mstate.init(prop.move_steps);
	}

	int get_id(){return id;}
	int get_owner_id(){return owner_id;}

	/*
	a bomb tick corresponds 1:1 to a game engine timestep.
	For bombs, ticks are used to determine when the bomb explodes.
	*/
	bool tick()
	{
		remaining_duration--;
		return (!remaining_duration);
		//if the 'duration' is zero, return true, signaling a detonation
	}

	bool near_explosion()
	{return (prop.duration * NEAR_CRITERION) >= (float)remaining_duration;}
}; 

/*
The powerup class.
The power_type_index is the only property - 
it determines what powerup it actually is.
*/
class PowerUp
{
public:
	int id;
	int power_type_index;
	Coordinates coords;
};

/*
Structure used for passing explosion "shape".
(explosions can be limited by a bomb's power property 
or by bricks/blocks/bombs they run into.
*/
struct ExplosionDat
{
	Coordinates center;
	unsigned int dN, dS, dW, dE;
	//the distance north, south, west, east of the explosion

	ExplosionDat()
	{dN = dS = dW = dE = 0;}
};

/*
Class for maintaining the (mostly cosmetic) explosion data 
for use by the UI.
However, this data is also passed to the agent AIs and may 
be interpreted usefully, for example to infer an opponents 
bomb's power.

An explosion is present for some "total_duration", however 
the explosion only impacts other objects the instant it happens, 
thus this data, as mentioned, is mostly cosmetic.
*/
class Explosion
{
public:
	unsigned int remaining_duration;
	unsigned int total_duration;

	ExplosionDat explosion_data;

	Explosion(ExplosionDat in_data)
	{
		remaining_duration = total_duration = DEFAULT_EXPLOSION_DURATION;
		explosion_data = in_data;
	}

	bool tick()
	{
		remaining_duration--;
		return (!remaining_duration);
		//if the 'duration' is zero, return true, signaling a fade
	}
};







/*********************************
The following are structures used to 
convert data to forms used by the VM.
Usually narrows classes:
Bomber -> VMBomber culls a large amount of 
data that shouldn't or doesn't need to
be known by other agents.
**********************************/

struct VMBomber
{
	unsigned int id;
	unsigned int team_id;
	Coordinates coords;
};

struct VMBomb
{
	bool is_callers;
	Coordinates coords;
};

struct VMPowerUp
{
	unsigned int type;
	Coordinates coords;
};

struct VMTile
{
	int type;
};

struct VMBomberStatus
{
	Coordinates position; //coords
	int player_id;    //id
	int team_id;      //team_id
	int move_rate;    //mstate.move_steps
	bool has_kick;    //prop.kicking
	bool has_detonate; //prop.detonation
	int radius; //bomb_prop.power
	int max_bombs; //prop.max_bombs
	int deployed_bombs; //nbombs
};

struct VMInput
{
	vector<VMBomber> vm_bombers;
	vector<VMBomb> vm_bombs;
	vector<VMPowerUp> vm_powerups;
	unsigned int H,W;
	VMTile * vm_tiles; //access is based on i*H + j
	VMBomberStatus vm_bomber_status;

	VMInput()
	{vm_tiles = 0;};
};

#endif